package com.denghq.projectbuilder.common.util;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

@Slf4j
public class BeanTool {

    private static ObjectMapper MAPPER = new ObjectMapper();

    static {
        DateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        TimeZone zone = TimeZone.getTimeZone("GMT+8");
        sf.setTimeZone(zone);
        MAPPER.setDateFormat(sf);
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) {
        if (map == null)
            return null;
        try {
            return MAPPER.readValue(MAPPER.writeValueAsString(map), beanClass);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("{}数据转换错误:{}", map, e.getMessage());
            return null;
        }
    }

    public static Map<?, ?> objectToMap(Object obj) {
        if (obj == null)
            return null;
        try {
            return MAPPER.readValue(objectToJsonStr(obj), HashMap.class);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("{}数据转换错误:{}", obj, e.getMessage());
            return null;
        }
    }

    public static String objectToJsonStr(Object obj) {
        if (obj == null)
            return null;
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("{}数据转换错误:{}", obj, e.getMessage());
            return null;
        }
    }

    public static <T> T jsonStrToObject(String json, Class<T> valueType) {
        try {
            return MAPPER.readValue(json, valueType);
        } catch (Exception e) {
            log.error("{}数据转换错误:{}", json, e.getMessage());
        }
        return null;
    }

    public static <T> T jsonStrToObject(String json, TypeReference typeReference) {
        try {
            return MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
            log.error("{}数据转换错误:{}", json, e.getMessage());
        }
        return null;
    }
    /**
     *  模仿Spring中 BeanUtils.copyProperties(source,target)
     *类型不同不可以转换
     *  但是
     * 大小写可以忽略
     * 下划线 _ 被忽略
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T copyProperties(Object source, Object target) {
        Map<String, Field> sourceMap = CacheFieldMap.getFieldMap(source.getClass());
        CacheFieldMap.getFieldMap(target.getClass()).values().forEach((it) -> {
            Field field = sourceMap.get(it.getName().toLowerCase().replace("_", ""));
            if (field != null) {
                it.setAccessible(true);
                field.setAccessible(true);
                try {
                    it.set(target,field.get(source));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        return (T) target;
    }

    private static class CacheFieldMap {

        private static Map<String, Map<String, Field>> cacheMap = new HashMap<>();

        private static Map<String, Field> getFieldMap(Class clazz) {
            Map<String, Field> result = cacheMap.get(clazz.getName());
            if (result == null) {
                synchronized (CacheFieldMap.class) {
                    if (result == null) {
                        Map<String, Field> fieldMap = new HashMap<>();
                        for (Field field : clazz.getDeclaredFields()) {
                            fieldMap.put(field.getName().toLowerCase().replace("_", ""), field);
                        }
                        cacheMap.put(clazz.getName(), fieldMap);
                        result = cacheMap.get(clazz.getName());
                    }
                }
            }
            return result;
        }
    }
}
